'''
Created on Apr 6, 2011

@author: Jonathan Herbst
'''

import re, socket
from threading import Thread
from SocketServer import UDPServer, BaseRequestHandler

from Utilities import LogFile, RandomKey
import Messages

class NOPortException(Exception): pass
class SessionException(Exception): pass

class Session():

    # session storage: <key> <ipAddr> <serverPort>
    _sessionRE = re.compile("([\w\d]+)\s+([\d\.]+)")
    _sessionTemplate = "%s %s"

    def __init__(self, sessionStr):
        self.key = self.ipAddr = None
        matchObject = self._sessionRE.match(str(sessionStr).strip())
        if matchObject is not None:
            self.key = matchObject.group(1)
            self.ipAddr = matchObject.group(2)
        
    def encodeSession(self):
        return self._sessionTemplate % (self.key, self.ipAddr)

class AppServer():
    
    _appServer = None
    _messageQueue = None
    _session = None
    _config = None
    _acceptSubscriber = False
    _log = None

    def __init__(self, messageQueue, config):
        AppServer._messageQueue = messageQueue
        AppServer._session = Session(config.pcAppSession)
        AppServer._config = config
        AppServer._log = LogFile("PCAppServer")
        AppServer._appServer = AppServer._PCAppServer(config.pcAppPort)
        
    def Broadcast(self, message):
        self._log.Write("Server", "Sending broadcast message")
        host = self._session.ipAddr
        port = int(self._config.pcAppClientPort)
        if not host or not port:
            self._log.Write("Server", "Missing host or port, can't broadcast", LogFile.ERROR)
            return False
        
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.sendto(message, (host, port))
        self._log.Write("Server", "Broadcast message sent")
        return True
    
    def Stop(self):
        self._appServer.Stop()
        
    def UpdateSession(self, key=None, iPAddress=None):
        if key: self._session.key = key
        if iPAddress: self._session.iPAddress = iPAddress
    
    class _PCAppHandler(BaseRequestHandler):
    
        _acceptableRXMessages = [Messages.PCAppLogin,
                                 Messages.DoorCommand,
                                 Messages.SetUserName,
                                 Messages.SetPassword,
                                 ]
        
        def _ValidateKey(self, key):
            if not key:
                return False
            if key != AppServer._session.key:
                return False
            return True
        
        def _ValidateLogin(self, userName, password):
            print "validating: %s %s with %s %s" % (userName, password, AppServer._config.pcAppUserName, AppServer._config.pcAppPassword)
            if userName != AppServer._config.pcAppUserName or password != AppServer._config.pcAppPassword:
                return False
            return True
        
        def _NewSession(self, macAddr, ipAddr, port):
            pass
            
        def handle(self):
            print "receiving request"
            AppServer._log.Write("PCAppHandler", "Receiving request")
            ipAddr = self.client_address[0]
            sock = self.request[1]
            
            response = None
            
            data = str(self.request[0]).strip()
            message = Messages.parseMessage(data)
            if message is not None:
                if message.__class__ in self._acceptableRXMessages:
                    if message.__class__ == Messages.PCAppLogin:
                        if self._ValidateLogin(message.userName, message.password):
                            AppServer._session.key = RandomKey()
                            AppServer._session.ipAddr = ipAddr
                            AppServer._config.pcAppSession = AppServer._session.encodeSession()
                            response = Messages.PCAppLoginSuccess(AppServer._session.key)
                        else:
                            response = Messages.PCAppLoginFailed()
                    elif self._ValidateKey(message.key):
                        self._Command(message)
            
            if response is not None:
                sock.sendto(response.renderMessage(), (ipAddr, int(AppServer._config.pcAppClientPort)))
                    
        def _Command(self, message):
            if message.__class__ is Messages.DoorCommand:
                AppServer._messageQueue.Push(Messages.PCAppDoorCommand(message))
            elif message.__class__ is Messages.SetUserName:
                AppServer._config.pcAppUserName = message.newUserName
            elif message.__class__ is Messages.SetPassword:
                AppServer._config.pcAppPassword = message.newPassword

    class _PCAppServer(Thread):
        
        def __init__(self, pcAppPort):
            
            try:
                self.m_port = int(pcAppPort)
                if not self.m_port:
                    raise NOPortException()
            except:
                raise NOPortException()
            
            Thread.__init__(self)
            self.daemon = True
            
            AppServer._log.Write("Server", "Starting")
            self.m_server = UDPServer(("0.0.0.0", self.m_port), AppServer._PCAppHandler)
            self.start()
            AppServer._log.Write("Server", "Started")
        
        def run(self):
            self.m_server.serve_forever()
        
        def Stop(self):
            self.m_server.shutdown()
            self.join()
